home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / freeWAIS-sf-1.1 / ir / irsearch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-21  |  46.1 KB  |  1,627 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.    
  4.    Brewster@think.com
  5. */
  6.  
  7. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  8.  
  9.  
  10. /* Looks up words in the inverted file index.
  11.  *
  12.  * Important functions:
  13.  * run_search
  14.  * search_for_words
  15.  *
  16.  * to do:
  17.  *    Handle searches on multiple databases
  18.  */
  19.  
  20. /* Change Log:
  21.  * $Log: irsearch.c,v $
  22.  * Revision 1.22  1994/12/22  13:59:37  pfeifer
  23.  * typo
  24.  *
  25.  * Revision 1.21  1994/09/07  13:29:22  pfeifer
  26.  * ctype is now included from cdialect.h after inclusion of string.h.
  27.  * Since ctype.h (the local version defines strcmp and friends, inclusion o
  28.  * of string.h after that caused probems
  29.  *
  30.  * Revision 1.20  1994/09/02  14:34:21  pfeifer
  31.  * fixed overlapping memory copies
  32.  *
  33.  * Revision 1.19  1994/08/23  12:33:12  pfeifer
  34.  * moved the HEADLINE constants to Defaults.tmpl
  35.  *
  36.  * Revision 1.18  1994/08/05  07:12:19  pfeifer
  37.  * Release beta 04
  38.  *
  39.  * Revision 1.17  1994/07/22  12:30:05  huynh1
  40.  * left for holidays
  41.  *
  42.  * Revision 1.16  1994/07/13  07:52:36  huynh1
  43.  * Uli
  44.  *
  45.  * Revision 1.15  1994/06/22  07:29:19  pfeifer
  46.  * Do not normalize weights
  47.  *
  48.  * Revision 1.14  1994/05/19  12:53:48  huynh1
  49.  * relevance feedback updated for fields.
  50.  *
  51.  * Revision 1.13  1994/05/18  16:44:02  huynh1
  52.  * patchlevel 09
  53.  *
  54.  * Revision 1.11  1994/04/06  23:52:04  huynh1
  55.  * 08, autoconf, Uli
  56.  *
  57.  * Revision 1.10  1994/03/23  15:30:58  pfeifer
  58.  * fixed iso code
  59.  *
  60.  * Revision 1.9  1994/03/23  12:59:57  huynh1
  61.  * calling openDatabase modified.
  62.  * insert open_global_Database.
  63.  * patchlevel 07.
  64.  *
  65.  * Revision 1.8  1994/03/08  20:43:18  huynh1
  66.  * Patchlevel 04
  67.  *
  68.  * Revision 1.7  1994/02/27  15:57:32  huynh1
  69.  * *** empty log message ***
  70.  *
  71.  * Revision 1.6  1994/02/14  10:32:21  huynh1
  72.  * new code for field concept added.
  73.  *
  74.  * Revision 1.5  1993/10/17  15:38:50  huynh1
  75.  * new code added for soundex, phonix retrieval and
  76.  * nested boolean queries.
  77.  *
  78.  * Revision 1.3  93/07/21  18:46:35  warnock
  79.  * Added STELAR-specific patches
  80.  * 
  81.  * Revision 1.2  93/07/02  18:04:26  warnock
  82.  * replace handle_next_and_previous for multi-type from francois
  83.  * 
  84.  * Revision 1.1  1993/02/16  15:05:35  freewais
  85.  * Initial revision
  86.  *
  87.  * Revision 1.54  92/05/10  14:44:35  jonathan
  88.  * Made a little safer on NULL docid's when parsing.
  89.  * 
  90.  * Revision 1.53  92/05/04  17:20:11  jonathan
  91.  * Added test for parsing docids (if null, log error).
  92.  * 
  93.  * Revision 1.52  92/04/29  08:22:17  shen
  94.  * declare global variable "_BE_normalized" to allow turning on/off FE score
  95.  * normalization.
  96.  * 
  97.  * Revision 1.51  92/04/28  16:56:30  morris
  98.  * added boolean to serial engine
  99.  * 
  100.  * Revision 1.50  92/04/01  17:10:21  jonathan
  101.  * ?
  102.  * 
  103.  * Revision 1.49  92/03/23  13:26:27  shen
  104.  * add timing for query. Compile with GET_QUERY_TIMING. print timing every 200 queries.
  105.  * 
  106.  * Revision 1.48  92/03/18  08:56:00  jonathan
  107.  * Removed databaseName argument to getDocumentText and getData.
  108.  * 
  109.  * Revision 1.47  92/02/17  16:22:42  jonathan
  110.  * Added WCAT to types that can be used for relevance feedback.
  111.  * 
  112.  * Revision 1.46  92/02/16  18:04:38  jonathan
  113.  * Demoted more WLOG_ERROR's to WLOG_WARNING's
  114.  * 
  115.  * Revision 1.45  92/02/16  09:51:12  jonathan
  116.  * Plugged some memory leaks.  I be there are more.
  117.  * 
  118.  * Revision 1.44  92/02/15  19:41:20  jonathan
  119.  * Improved logging for invalid relevant documents.
  120.  * 
  121.  * Revision 1.43  92/02/14  16:06:48  jonathan
  122.  * Added diagnostic record for invalid relevant document.
  123.  * 
  124.  * Revision 1.42  92/02/12  17:30:20  jonathan
  125.  * Conditionalized inclusion of object code.
  126.  * 
  127.  * Revision 1.41  92/02/12  17:04:03  jonathan
  128.  * Moved logging info around.
  129.  * 
  130.  * Revision 1.40  92/02/12  15:26:35  morris
  131.  * only call fnished_search_word when the preceeding search was successful
  132.  * 
  133.  * Revision 1.39  92/02/12  13:30:39  jonathan
  134.  * Added "$Log" so RCS will put the log message in the header
  135.  * 
  136.  * changes 5.2.90 HWM
  137.     - changed calls to perror() to calls to panic()
  138.     - made print_best_hits() only print hits w/ non-zero weight
  139.     - made random arrays static instead of reading them in.  
  140.       removed getRandomArray.
  141.     - removed unused variables
  142.   Brewster 7/90 made look_up_word_in_dictionary safer.
  143.   Brewster 7/90 elimiated trailing <lf> on filename and headline table accesses
  144.   HWM 7.12.90 - replaced all calls to panic with error code returns and a log
  145.                 file  
  146.           - added the routine initSearchEngine() which should be called 
  147.             before any other search routine
  148.           - added beFriendly() to give other processes time under 
  149.             multifinder
  150.   JG 5.31.91 - added relevance feedback for line fragments.
  151.   JG 7.8.91  - added doc_id to search_for_words, removed scale_scores.
  152. */
  153.  
  154. #if 0
  155. #define GET_QUERY_TIMING
  156. #endif
  157.  
  158. #define _search_c
  159.  
  160.  
  161. /* #include <string.h>     /* for strlen() */
  162. #ifdef THINK_C
  163. #include <unix.h>         /* for sleep() */
  164. #endif /* think_c */
  165.  
  166. #include "cutil.h"
  167. #include "irfiles.h"
  168. #include "irtfiles.h" /* for map_over_words */
  169. #include "irext.h"
  170. #include "irsearch.h"
  171. #include "docid.h"
  172. #include <math.h>
  173. #include "irretrvl.h"
  174. #ifdef BOOL
  175. #include "irparse.h"
  176. #endif
  177. #ifdef SOUND
  178. #include "soundex.h"
  179. #endif
  180.  
  181. #ifdef NESTED_BOOLEANS /* tung , 10/93 */
  182. #include "analyse_str.h"
  183. #endif
  184.  
  185.  
  186. #ifdef FIELDS /* tung, 1/94 */
  187. #include "field_search.h"
  188. #endif
  189.  
  190. #include "trie.h"
  191.  
  192. #define TEST_SEARCH     false    /* set to TRUE to allow printing to console */
  193.  
  194. #ifdef STELAR
  195. /* File of associations between STELAR abstracts and bitmaps */
  196. FILE *BITMAPS;
  197. #endif /* STELAR */
  198.  
  199. char *server_name = NULL;
  200. long tcp_port = 0;
  201.  
  202. long _BE_normalized = 1;
  203.  
  204. #ifdef GET_QUERY_TIMING
  205. #include <sys/timeb.h>
  206. static struct timeb  s_time, e_time;
  207. static float t_time = 0;
  208. static long n_query = 0;
  209. #endif
  210.  
  211.  
  212. /*----------------------------------------------------------------------*/
  213.  
  214. static Boolean calcDocLength _AP((hit* theHit,long* lines,long* bytes));
  215.  
  216. static Boolean
  217. calcDocLength(theHit,lines,bytes)
  218. hit* theHit;
  219. long* lines;
  220. long* bytes;
  221. /* Given a hit, open the file and figure out how many bytes and lines
  222.    it contains.  This is not needed by the serial search engine (it
  223.    stores these values in its dictionary.  It is used by the dynamic
  224.    help facility).
  225. */
  226. {
  227.   *lines = theHit->number_of_lines;
  228.  
  229.   /* find the length of the document */
  230.   if(theHit->end_character != 0)
  231.     {
  232.       /* document is not whole file, so size is stored */
  233.       *bytes = theHit->end_character - theHit->start_character;
  234.       return(true);
  235.     }
  236.   else
  237.     {    
  238.       /* whole file, find file length from the file */
  239.       FILE* file = NULL;
  240.       if (((file = s_fopen(theHit->filename, "r")) != NULL) &&
  241.       (s_fseek(file, 0L, SEEK_END) == 0)  &&
  242.       ((*bytes = ftell(file)) != -1))
  243.     { s_fclose(file);
  244.       return(true);        /* we are done, bytes is set */
  245.     }
  246.       else
  247.     { s_fclose(file);
  248.       return(false);    /* something went wrong with the file */
  249.     }
  250.     }
  251. }
  252.  
  253.  
  254.  
  255. static long search_word_before_pairs _AP((char *word, 
  256. #ifdef FIELDS /* tung, 5/94 */
  257.                       char* field_name,
  258. #endif
  259.                       long char_pos,
  260.                       long line_pos, long weight,
  261.                       long doc_id, time_t doc_date,
  262.                       boolean capitalized, database* db));
  263.  
  264. /* returns 0 is successful, non-0 if error.  A copy of add_word_before_pairs */
  265. static long search_word_before_pairs (word, 
  266. #ifdef FIELDS /* tung, 5/94 */
  267.                       field_name,
  268. #endif          
  269.                       char_pos, line_pos,
  270.                       weight, doc_id, doc_date, capitalized, db)
  271.      char *word;    /* the word to be indexed, this could be a
  272.                word pair. If NULL there are no more words
  273.                to be indexed */
  274. #ifdef FIELDS /* tung, 5/94 */
  275.      char *field_name;
  276. #endif
  277.      long char_pos;    /* the position of the start of the
  278.                word */
  279.      long line_pos;    /* this is passed for the best
  280.                section calculation */
  281.      long weight;    /* how important the word looks
  282.                syntactically (such as is it bold)
  283.                NOT used by signature system */
  284.      long doc_id;     /* current document, this will never be 0 */
  285.      time_t doc_date; /* display day of this document, 0 if not known */
  286.      boolean capitalized; /* if the word started with a cap */
  287.      database* db; /* database to insert the document */
  288. {
  289.   static char last_word[MAX_WORD_LENGTH + 1];
  290.   static long last_doc_id = -1;
  291.   /* The way it works is it remembers if the last word if it was
  292.      capitalized (if not it clears the saved word).  
  293.      If another capitalized word comes along next
  294.      (and it is in the same document), then it makes a joint word and calls 
  295.      add_word with it. */
  296.   if(capitalized){
  297.     if(last_word[0] != '\0' && last_doc_id == doc_id){
  298.       search_word(make_joint_word(last_word, word), 
  299. #ifdef FIELDS /* tung, 5/94 */
  300.           field_name,
  301. #endif
  302.           char_pos, line_pos, weight, doc_id, 1L, db);
  303.     }
  304.     else{
  305.       last_word[0] = '\0';
  306.     }
  307.     strncpy(last_word, word, MAX_WORD_LENGTH);
  308.     last_doc_id = doc_id;
  309.   }
  310.   else{ /* not capitalized */
  311.     last_word[0] = '\0';
  312.   }
  313.   return(search_word(word, 
  314. #ifdef FIELDS /* tung, 5/94 */
  315.              field_name,
  316. #endif         
  317.              char_pos, 
  318.              line_pos, weight, doc_id, 0L, db));
  319. }
  320.  
  321. long count_trie_words;
  322. long count_uniq;
  323.  
  324. boolean prepare_word_list(words,set,alloc)
  325.      char* words;
  326.      trie* set;
  327.      trie_allocator* alloc;
  328. {
  329.   char* word = NULL;
  330.   int * datum;
  331.   count_trie_words = count_uniq = 0;
  332.   /* printf("words: %s\n", words); */
  333. #ifdef BIO
  334.   word = (char*)strtokf(words,wordDelimiter);
  335. #else
  336.   word = (char*)strtokf_isalnum(words);
  337. #endif
  338.   while(word != NULL){
  339.     long dictionary_value;
  340.     /* trim the string if necessary */
  341.     if(strlen(word) > MAX_WORD_LENGTH){
  342.       word[MAX_WORD_LENGTH] = '\0';
  343.     }
  344.  
  345.     if(!encode((unsigned char*)word)) {
  346.       panic("can't encode word %s",word);
  347.     }
  348.     datum = (int *)trie_lookup(word,set,alloc);
  349.     if(!datum) {
  350.       panic("trie_lookup failed !!!");
  351.     }
  352.  
  353.       count_trie_words++;
  354.  
  355.     *datum += 1;
  356.  
  357.       if (*datum == 1 ) {
  358.       count_uniq++;
  359.       }
  360. #ifdef BIO
  361.     word = (char *)strtokf(NULL,wordDelimiter);
  362. #else
  363.     word = (char *)strtokf_isalnum(NULL);
  364. #endif
  365.     beFriendly();
  366.   } 
  367.  
  368.   waislog(WLOG_LOW, WLOG_INFO,
  369.       "after preparing word list, %d search items were presented.",
  370.       count_trie_words);
  371.   waislog(WLOG_LOW, WLOG_INFO, 
  372.       "There are %d words to search for.",
  373.       count_uniq);
  374.  
  375.   return(true);
  376. }
  377.  
  378. #ifdef FIELDS /* tung, 5/94 */
  379. boolean search_for_trie_words(dict,db,prefix,docid,result, field_name)
  380. #else
  381. boolean search_for_trie_words(dict,db,prefix,docid,result)
  382. #endif
  383. trie* dict;
  384. database* db;
  385. char* prefix;
  386. long docid;
  387. boolean result;
  388. #ifdef FIELDS /* tung, 5/94 */
  389. char *field_name;
  390. #endif
  391. {
  392.   char buffer[MAX_WORD_LENGTH+1];
  393.   char tmp_word[MAX_WORD_LENGTH+1];
  394.   char* word;
  395.   long dictionary_value;
  396.   int weight;
  397.   char* tmp=word;
  398.   if (dict == NULL) {
  399.     return result;
  400.   }
  401.   if (*dict->string) {
  402.     strcpy(buffer,prefix);
  403.     strcat(buffer,dict->string);
  404.     word = buffer;
  405.   } else {
  406.     word = prefix;
  407.   }
  408.  
  409.   if (dict->datum) {
  410.     long number_of_occurrences;
  411.     /* this node has data */
  412.     strcpy(tmp_word,word);
  413.     decode(tmp_word);
  414. #ifdef FIELDS /* tung, 5/94 */
  415.     if(db->number_of_fields > 0) 
  416.       result |= search_word(tmp_word,field_name,0L,0L,FIELD_FLAG,docid,0L,db);
  417.     else result |= search_word(tmp_word,field_name,0L,0L,1L,docid,0L,db);
  418. #else
  419.     result |= search_word(tmp_word,0L,0L,1L,docid,0L,db);
  420. #endif
  421.   }
  422.   if (dict->table) {
  423.     int i;
  424.     int len;
  425.     len = strlen(word);
  426.     for (i=0;i<ALPHA_SIZE;i++) {
  427.       if(dict->table[i]) {
  428.     word[len]=(char)i;
  429.     word[len+1]='\0';
  430. #ifdef FIELDS /* tung, 5/94 */
  431.     result = search_for_trie_words(dict->table[i],db,word,docid,result,field_name);
  432. #else
  433.     result = search_for_trie_words(dict->table[i],db,word,docid,result);
  434. #endif
  435.       }
  436.     }
  437.   }
  438.   return result;
  439. }
  440.  
  441.  
  442. /* dgg -- pulled this from irtfiles.c:map_over_words */
  443. /* returns the number of words added, or -1 if an error occurred */
  444. long search_over_words
  445.   _AP((char* line,long document_id,database* db));
  446.        
  447. long search_over_words(line, document_id, db) 
  448. char* line;
  449. long document_id;
  450. database* db;
  451. {
  452.   long weight = 1L;
  453.   long file_position_before_line = 0;
  454.   boolean word_position= false;
  455.   boolean word_pairs= false;
  456. #ifdef BIO
  457.   int minwordlen= 1; /* only if symbols are active ? */
  458. #else
  459.   int minwordlen= 2;
  460. #endif
  461.  
  462.   long position_in_word = 0;
  463.   long word_count = 0;
  464.   unsigned long ch;
  465.   long char_count = 0;
  466.   boolean capitalized = false; /* if the word starts with a cap */
  467. #ifdef FIELDS /* tung, 7/94 */
  468.   char word[MAX_FIELD_LENGTH + 1];
  469. #ifdef LITERAL
  470.   char  key;
  471. #endif /* LITERAL */
  472. #else
  473. #ifdef LITERAL
  474.   char  word[MAX_PHRASE_LENGTH + 1];
  475.   char  key;
  476. #else
  477.   char word[MAX_WORD_LENGTH + 1];
  478. #endif /* LITERAL */
  479. #endif /* FIELDS */  
  480. #ifdef BOOLEANS
  481. #define MAX_LINE_LENGTH 1000  
  482.   boolean  nextIsNot = false;
  483.   char  notwords[MAX_LINE_LENGTH+1];
  484. #endif
  485. #ifdef SOUND
  486.   boolean nextIsSoundex = false;
  487.   boolean nextIsPhonix = false;
  488. #endif
  489. #ifdef FIELDS /* tung, 5/94 */
  490.   char field_name[MAX_WORD_LENGTH + 1];
  491.   field_name[0] = '\0';
  492. #endif
  493.  
  494. #ifdef BOOLEANS
  495.   notwords[0]= '\0';
  496. #endif
  497.  
  498.   for(ch = (unsigned char)line[char_count++]; 
  499.       ch != '\0'; 
  500.       ch = (unsigned char)line[char_count++]){ /* Purify (abr):  (up) */
  501. #ifdef BIO
  502.     boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  503. #else
  504.     boolean alnum = isalnum(ch);
  505. #endif
  506. #if 0
  507. #ifdef PARTIALWORD
  508.     if (ch == PARTWORD_WILDCARD) { 
  509.       alnum= true; minwordlen= MAXIMUM(2,minwordlen); 
  510.       weight = PARTIAL_FLAG;
  511.     }
  512. #endif
  513. #endif
  514. #ifdef LITERAL
  515.    if (ch == LITERAL_KEY1)  key= LITERAL_KEY1;
  516.    else if (ch == LITERAL_KEY2)  key= LITERAL_KEY2;
  517.    else {key= 0; weight = 1L;}  /* by only key=0 is it not possible
  518.                                    combine literal search and boolean search
  519.                                    */
  520.    if (key != 0) {
  521.      char *cp, *match;
  522.      cp = line + char_count;  
  523.      match = strchr( cp, key); 
  524. /* printf("search_over_words: literal key is [%c]\n", key); */
  525.       if (match != NULL && cp < match) {
  526.       for (position_in_word=0; cp < match; cp++, char_count++)
  527.             if (position_in_word < MAX_PHRASE_LENGTH) {
  528.          word[position_in_word++] = char_downcase((unsigned long)*cp);
  529.          }
  530.           char_count++; /* skip closing key */
  531.       alnum= false;
  532.       capitalized= false;
  533.       weight= LITERAL_FLAG;   /* is this a safe flag parameter? -- 
  534.           unused but passed on to search_word is what we need */
  535.     /* !! need to break literal "word" into 1st dictionary word and search
  536.         on that... */
  537. /* printf("search_over_words: literal is [%s]\n", word); */
  538.         }
  539.       }
  540. #endif
  541. #ifdef PARTIALWORD
  542.     if (ch == PARTWORD_WILDCARD) { 
  543.       word[position_in_word++] = ch;
  544.       ++char_count;
  545.       alnum= false; minwordlen= MAXIMUM(2,minwordlen);
  546.       weight = PARTIAL_FLAG;
  547.     }
  548.     else
  549. #endif
  550.  
  551. #ifdef FIELDS /* tung, 1/94 */
  552.     if((ch == EQ) ||
  553.        (ch == GREATER) ||
  554.        (ch == LESS)) { /* it's field ? */
  555.       if(position_in_word >= minwordlen){ /* is it reasonable ? */
  556.     long field_id;
  557.     word[position_in_word] = '\0';
  558.     strncpy(field_name, word, MAX_WORD_LENGTH);
  559.     field_name[position_in_word] = '\0';
  560.     field_id = pick_up_field_id(field_name, db);
  561.         if((ch == EQ && line[char_count] == EQ) ||
  562.            ch == GREATER || ch == LESS) {
  563.           char *cp;
  564.           
  565.       if(ch == EQ && line[char_count] == EQ)
  566.             char_count += 2;
  567.           else ++char_count;
  568.           cp = line + char_count; 
  569.           position_in_word = 0;
  570.           word[position_in_word++] = (unsigned long)ch;
  571.           for (position_in_word=1; (*cp != '\0') && !isspace(*cp); cp++, char_count++) {
  572.             word[position_in_word++] = char_downcase((unsigned long)*cp);
  573.       }
  574.       if(*cp != '\0')
  575.         char_count++;
  576.           alnum= false;
  577.           capitalized= false;
  578.           weight = NUMERIC_FLAG;
  579.         }
  580.         else {
  581.           position_in_word = 0;
  582.           char_count++;
  583.           alnum= false;
  584.           capitalized= false;
  585.           weight = FIELD_FLAG;
  586.         }
  587.       }
  588.     }
  589. #endif
  590.     if(alnum){
  591.       /* put the character in the word if not too long */
  592.       if(position_in_word == 0)
  593.     capitalized = isupper((unsigned long)ch)?true:false;
  594.       if(position_in_word < MAX_WORD_LENGTH){
  595.     word[position_in_word++] = char_downcase((unsigned long)ch);
  596.       }
  597.     }
  598.     else{ /* not an in a word */
  599.       if(position_in_word != 0){
  600. #ifdef BOOLEANS
  601. /* note on BOOLEANS -- we really need to check for NOT words here,
  602.    and move them to back of line so that (wordfunction)== search_word is
  603.    called for NOT words after other words (excluding NOT inside a literal)
  604. */
  605.     if (nextIsNot) {
  606.       word[position_in_word] = '\0';
  607.       strcat( notwords, word);
  608.       strcat( notwords, " ");
  609.       nextIsNot= false;
  610.        word_count++;
  611.       }
  612.     else if ((strncmp(word,BOOLEAN_NOT,position_in_word)==0)) {
  613. #ifdef NESTED_BOOLEANS
  614.           word[position_in_word] = '\0';
  615.           if(0 != search_word_before_pairs
  616.              (word,
  617. #ifdef FIELDS /* tung, 5/94 */
  618.           field_name,
  619. #endif
  620.               file_position_before_line + char_count,  
  621.               /*^^  this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  622.               0L, /* line_pos */
  623.               weight, 
  624.               document_id, 
  625.               (time_t)0L,
  626.               capitalized,
  627.               db))  
  628.             return(-1); /* error */
  629.           nextIsNot= false;
  630.           word_count++;
  631. #else
  632.           nextIsNot= true;
  633.           word_count++;
  634. #endif
  635.         }
  636.     else
  637. #endif
  638. #ifdef SOUND
  639.           if (nextIsSoundex) {
  640.             static char Key[80];
  641.             word[position_in_word] = '\0';
  642.             SoundexCode(word,Key);
  643. #ifdef DEBUG
  644.             printf("search_over_words: adding Soundex (%s,%s)\n", word, Key);
  645. #endif
  646.             if(0 != search_word_before_pairs
  647.                (Key,
  648. #ifdef FIELDS /* tung, 5/94 */
  649.           field_name,
  650. #endif
  651.                 file_position_before_line + char_count, 
  652.                 0L,    /* line_pos */
  653.                 weight, 
  654.                 document_id, 
  655.                 (time_t)0L,
  656.                 capitalized,
  657.                 db))
  658.               return(-1);
  659.             word_count++; nextIsSoundex = false;
  660.           } else if ((strncmp(word,SOUNDEX,strlen(SOUNDEX))==0)) {
  661.             nextIsSoundex= true;
  662.             word_count++;
  663.       }
  664.           else if (nextIsPhonix) {
  665.             char Key[80];
  666.             word[position_in_word] = '\0';
  667.             PhonixCode(word,Key);
  668. #ifdef DEBUG
  669.             printf("search_over_words: adding Phonix (%s,%s)\n", word, Key);
  670. #endif
  671.             if(0 != search_word_before_pairs
  672.                (Key,
  673. #ifdef FIELDS /* tung, 5/94 */
  674.           field_name,
  675. #endif
  676.                 file_position_before_line + char_count, 
  677.                 0L,    /* line_pos */
  678.                 weight, 
  679.                 document_id, 
  680.                 (time_t)0L,
  681.                 capitalized,
  682.                 db))
  683.               return(-1);
  684.             word_count++; nextIsPhonix = false;
  685.           } else if ((strncmp(word,PHONIX,strlen(PHONIX))==0)) {
  686.             nextIsPhonix= true;
  687.             word_count++;
  688.           } else
  689. #endif
  690.     /* then we have collected a word */
  691.     if(position_in_word >= minwordlen){ /* is it reasonable ? */
  692.       word[position_in_word] = '\0';
  693.       if(0 != search_word_before_pairs(word,
  694. #ifdef FIELDS /* tung, 5/94 */
  695.                        field_name,
  696. #endif
  697.                  file_position_before_line + char_count,  
  698.                  /*^^  this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  699.                  0L, /* line_pos */
  700.                  weight, 
  701.                  document_id, 
  702.                  (time_t)0L,
  703.                  capitalized,
  704.                  db))  
  705.         return(-1); /* error */
  706. #ifdef BOOLEANS
  707.       nextIsNot= false;
  708. #endif
  709. #ifdef SOUND
  710.               nextIsPhonix = false;
  711.               nextIsSoundex = false;
  712. #endif
  713.        word_count++;
  714.     }
  715.     position_in_word = 0;
  716.       }
  717.     }
  718.   }
  719.   
  720.   /* finish last word */
  721. #ifndef NESTED_BOOLEANS /* 10/93 */
  722. #ifdef BOOLEANS
  723.   if (nextIsNot) {
  724.     word[position_in_word] = '\0';
  725.     strcat( notwords, word);
  726.     strcat( notwords, " ");
  727.     nextIsNot= false;
  728.     word_count++;
  729.     }
  730.  else 
  731. #endif
  732. #endif
  733. #ifdef SOUND
  734.     if (nextIsSoundex) {
  735.     static char Key[80];
  736.     word[position_in_word] = '\0';
  737.     SoundexCode(word,Key);
  738. #ifdef DEBUG
  739.     printf("search_over_words: adding Soundex (%s,%s)\n", word, Key);
  740. #endif
  741.     if(0 != search_word_before_pairs(Key,
  742. #ifdef FIELDS /* tung, 5/94 */
  743.                      field_name,
  744. #endif
  745.                                      file_position_before_line + char_count, 
  746.                                      0L,    /* line_pos */
  747.                                      weight, 
  748.                                      document_id, 
  749.                                      (time_t)0L,
  750.                                      capitalized,
  751.                                      db))
  752.       return(-1);
  753.     word_count++;
  754.     }
  755.     else if (nextIsPhonix) {
  756.       char Key[80];
  757.       word[position_in_word] = '\0';
  758.       PhonixCode(word,Key);
  759. #ifdef DEBUG
  760.       printf("search_over_words: adding Phonix (%s,%s)\n", word, Key);
  761. #endif
  762.       if(0 != search_word_before_pairs(Key,
  763. #ifdef FIELDS /* tung, 5/94 */
  764.                        field_name,
  765. #endif
  766.                                        file_position_before_line + char_count, 
  767.                                        0L,    /* line_pos */
  768.                                        weight, 
  769.                                        document_id, 
  770.                                        (time_t)0L,
  771.                                        capitalized,
  772.                                        db))
  773.       return(-1);
  774.       word_count++;
  775.     }
  776.   else
  777. #endif
  778.  if(position_in_word >= minwordlen){ /* is it reasonable ? */
  779.     word[position_in_word] = '\0';
  780.     if(0 != search_word_before_pairs(word,
  781. #ifdef FIELDS /* tung, 5/94 */
  782.                      field_name,
  783. #endif
  784.                      file_position_before_line + char_count, 
  785.                      0L,    /* line_pos */
  786.                      weight, 
  787.                      document_id, 
  788.                      (time_t)0L,
  789.                      capitalized,
  790.                      db))  
  791.       return(-1);
  792.     word_count++;
  793.   }
  794.  
  795. #ifndef NESTED_BOOLEANS /* 10/93 */  
  796. #ifdef BOOLEANS
  797.   if ((notwords[0] != '\0')) { 
  798.     char wordn[MAX_WORD_LENGTH+1];   /* this is a copy of wordp */
  799.     char  *wordp;
  800.     capitalized= false;
  801.     char_count= 0;       /* !?? need char index for each word ? */
  802.     weight= BOOLEAN_NOT_FLAG; /* is this a safe parameter ? */   
  803.     wordp= strtok( notwords, " ");
  804.     while (wordp!=NULL) {
  805.       s_strncpy(wordn,wordp,MAX_WORD_LENGTH);
  806.       if(0 != search_word_before_pairs(wordn, /* wordp -> bug */
  807. #ifdef FIELDS /* tung, 5/94 */
  808.                        field_name,
  809. #endif       
  810.                                        file_position_before_line + char_count, 
  811.                                        0L,    /* line_pos */
  812.                                        weight, 
  813.                                        document_id, 
  814.                                        (time_t)0L,
  815.                                        capitalized,
  816.                                        db))  
  817.         return(-1);
  818.       wordp= strtok(NULL, " ");
  819.     }
  820.   }
  821. #endif
  822. #endif  
  823.   return(word_count);
  824. }
  825.  
  826.  
  827. boolean search_for_words(words, db, doc_id)
  828.      char* words;
  829.      /* break the string into words (using map_over_words)
  830.     and repeatedly call search_word_before_pairs(). 
  831.     Returns true if successful.
  832.     */
  833.      database *db;
  834.      long doc_id;  /* = 1 for words == document in relevance feedback search */
  835. {
  836.  
  837. #ifdef BOOL
  838.   /* LISP QUERY */
  839.   if( words[0] == '(' ){ /* then it is a lisp query */
  840.     /* this is a temporary stub for the real work */
  841.     char error_string[ERROR_STRING_LEN];
  842.     object* query = (object*)parseQuery(words,QUERY_SYNTAX_LISP,error_string);
  843.     if(query == NULL){
  844.       waislog(WLOG_HIGH, WLOG_ERROR, "Unparsable query %s", error_string);
  845.       return(false);
  846.     }
  847.     else{
  848.       query = (object*)send(query,Evaluate,db);
  849.       return(true);
  850.     }
  851.   }
  852. #endif  /* def BOOL */
  853.  
  854. /* dgg mods really need new version of map_over_words for searching only
  855.   (not for adding == indexing), and this way we can keep main search
  856.   routines here (irsearch.c) & search_word in sersrch.c
  857. */
  858.  
  859.   /* NORMAL QUERY */
  860.   if( -1 == search_over_words(words, doc_id, db))
  861.     return(false);
  862.   else
  863.     return(true); 
  864. }
  865.  
  866.  
  867. /* gets the next best hit from the search engine and fills in all the slots.
  868.    If the document does not exist, then it gets another, etc.
  869.    It returns 0 if successful */   
  870. long next_best_hit(the_best_hit, db)
  871.      hit *the_best_hit;
  872.      database *db;
  873. {
  874.   document_table_entry doc_entry;
  875.   long ret_value,start,end,length,date,nlines=0;
  876.   char headline[MAX_HEADLINE_LEN];
  877.   char filename[200],type[20];
  878.   while(1){ /* keep going until we get a good document */
  879.     if(0 != (ret_value = best_hit(db,&(the_best_hit->document_id),
  880.                   &(the_best_hit->best_character),
  881.                   &(the_best_hit->best_line),
  882.                   &(the_best_hit->weight),
  883.                   &start,&end,&date,&length,&nlines,
  884.                                   headline,filename,type))){
  885.       /* if we add start,end,length,date,headline here, we can remove lots of
  886.          disk reads */
  887.       
  888.       return(ret_value);
  889.     }
  890.     if(the_best_hit->weight <= 0.0)    /* if we are out of good stuff, return */
  891.       return(1);
  892.     /* fill in the rest of the hit */
  893.     the_best_hit->start_character = start;
  894.     the_best_hit->end_character = end;
  895.     the_best_hit->document_length = length;
  896.     the_best_hit->number_of_lines = nlines;
  897.     strcpy(the_best_hit->filename,filename);
  898.     strcpy(the_best_hit->type,type);
  899.     strcpy(the_best_hit->headline,headline);
  900.     sprintf(the_best_hit->date, "%d", date);
  901.     /* do we need this step??  JMF */
  902.     if (/*read_document_table_entry(&doc_entry, the_best_hit->document_id, db)*/true == true){
  903.       /*
  904.          the_best_hit->start_character = doc_entry.start_character;
  905.          the_best_hit->end_character = doc_entry.end_character;
  906.          the_best_hit->document_length = doc_entry.document_length;
  907.          the_best_hit->number_of_lines = doc_entry.number_of_lines;
  908.          sprintf(the_best_hit->date, "%d", doc_entry.date);
  909.          read_filename_table_entry(doc_entry.filename_id, 
  910.          the_best_hit->filename,
  911.          the_best_hit->type,
  912.          NULL,
  913.          db),
  914.          strncpy(the_best_hit->headline, 
  915.          read_headline_table_entry(doc_entry.headline_id,db),
  916.          MAX_HEADLINE_LEN);
  917.          */
  918.       if(probe_file_possibly_compressed(the_best_hit->filename))
  919.     return(0);
  920.       else {
  921.     waislog(WLOG_HIGH, WLOG_WARNING, 
  922.         "Dangling File %s in database %s.", 
  923.         the_best_hit->filename,
  924.         db->database_file);
  925.       }
  926.     }
  927.     else {
  928.       waislog(WLOG_HIGH, WLOG_ERROR, 
  929.           "Error reading doc_table_entry for database %s, docid: %ld",
  930.           db->database_file,
  931.           the_best_hit->document_id);
  932.     }
  933.     beFriendly();
  934.   }
  935. }
  936.  
  937. /*-New one---------------------------------------------------------------------*/
  938.  
  939. /* this function figures out if the request is for a NEXT or Previous document.
  940.    If it is, then it makes a header for it and returns it.  If not, then it 
  941.    returns NULL. */
  942.  
  943. WAISDocumentHeader*
  944. handle_next_and_previous(docs, db, waisProtocolVersion, server)
  945. DocObj** docs;
  946. database* db;
  947. long waisProtocolVersion;
  948. char* server;
  949. {
  950.   char* dbName = db->database_file;
  951.   WAISDocumentHeader* header;
  952.   DocID* theDocID = NULL;
  953.   char *local_id;
  954.  
  955.   long count,i;
  956.   char* tmp_type = NULL;   /* temporary type */
  957.   char* tmp_type_pointer = NULL;   /* temporary type pointer */
  958.  
  959.  
  960.   if(docs != NULL) { /* All of this is for WAIS_Prev and WAIS_next */
  961.     if(docs[0] != NULL && docs[0]->Type != NULL) {
  962.       long id = -1;
  963.  
  964.       if((theDocID = docIDFromAny(docs[0]->DocumentID)) == NULL) {
  965.     waislog(WLOG_HIGH, WLOG_WARNING, "can't parse docid");
  966.     return(NULL);
  967.       }
  968.  
  969.       local_id = anyToString(GetLocalID(theDocID));
  970.  
  971.       if(strcmp(docs[0]->Type,"WAIS_NEXT") == 0)        /* next page */
  972.     id = next_docid(local_id,db);
  973.       else if(strcmp(docs[0]->Type,"WAIS_PREV") == 0)        /* prev page */
  974.     id = previous_docid(local_id, db);
  975.  
  976.       freeDocID(theDocID); s_free(local_id);
  977.  
  978.       if (id > -1) {
  979.     document_table_entry doc_entry;
  980.     hit foo;
  981.     long lines,length;
  982.     char local_id[MAX_FILENAME_LEN + 60]; /* filename, start, end */
  983.  
  984.     local_id[0] = '\0';
  985.  
  986.     if (read_document_table_entry(&doc_entry, id, db) == true) {
  987.       foo.start_character = doc_entry.start_character;
  988.       foo.end_character = doc_entry.end_character;
  989.       foo.document_length = doc_entry.document_length;
  990.       foo.number_of_lines = doc_entry.number_of_lines;
  991.  
  992.       read_filename_table_entry(doc_entry.filename_id, 
  993.                     foo.filename,
  994.                     foo.type,
  995.                     NULL,
  996.                     db),
  997.       strncpy(foo.headline, 
  998.           read_headline_table_entry(doc_entry.headline_id,db),
  999.           MAX_HEADLINE_LEN);
  1000.       sprintf(foo.date, "%d", doc_entry.date);
  1001.       sprintf(local_id, "%ld %ld %s", 
  1002.           doc_entry.start_character,
  1003.           doc_entry.end_character,
  1004.           foo.filename);
  1005.         
  1006.       if(calcDocLength(&(foo),&lines,&length)){
  1007.         /* this document is good, return it */
  1008.         char** type = NULL;
  1009.         
  1010. /* multitype extensions */
  1011. /* 
  1012.    Need to parse out the document types and add them to the 
  1013.    document type list.
  1014. */
  1015.       if (waisProtocolVersion >= '2') {
  1016.         
  1017.         /* I left this conditional here (it is not really needed, the 
  1018.            'else' part could take care of both) on the assumption that
  1019.            it would be faster for single type documents.
  1020.         */ 
  1021.         if ( strstr(foo.type,",") == NULL ) {
  1022.            type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1023.            type[0] = s_strdup(foo.type);
  1024.            type[1] = NULL;
  1025.         }
  1026.         else {
  1027.            /* count up the number of document types */
  1028.            count = 1L;
  1029.            for (i = 0L; i < strlen(foo.type); i++){
  1030.           if ( foo.type[i] == ',' )
  1031.              count++;
  1032.            }
  1033.      
  1034.       
  1035.            /* allocate space for types */
  1036.            type = (char**)s_malloc((size_t)(sizeof(char*) * (count + 1L)));
  1037.            
  1038.            /* duplicate the type and save the pointer */
  1039.            tmp_type = s_strdup(foo.type);
  1040.            tmp_type_pointer = tmp_type;
  1041.     
  1042.           
  1043.            /* add types - NULL out the pointer so that strtok can grab the subsequent entries */
  1044.            for (i = 0L; i < count; i++ ) {
  1045.           type[i] = s_strdup(strtok(tmp_type_pointer,","));
  1046.           tmp_type_pointer = NULL;
  1047.            }
  1048.     
  1049.            /* add the terminating null */
  1050.            type[count] = NULL;
  1051.            
  1052.            /* release the tmp_type allocations */
  1053.            s_free(tmp_type);
  1054.            
  1055.         }
  1056.     
  1057.       }
  1058.            
  1059.         theDocID = makeDocID();
  1060.  
  1061.         theDocID->distributorServer = stringToAny(server); 
  1062.         theDocID->originalServer = stringToAny(server);    
  1063.         theDocID->distributorDatabase = stringToAny(dbName);
  1064.         theDocID->originalDatabase = stringToAny(dbName);
  1065.         theDocID->distributorLocalID = stringToAny(local_id);
  1066.         theDocID->originalLocalID = stringToAny(local_id);
  1067.  
  1068.         header=
  1069.           makeWAISDocumentHeader(anyFromDocID(theDocID),
  1070.                      UNUSED,
  1071.                      -1L,
  1072.                      UNUSED,
  1073.                      length,lines,
  1074.                      type,
  1075.                      s_strdup(dbName),
  1076.                      s_strdup(foo.date),
  1077.                      s_strdup(foo.headline),
  1078.                      NULL);
  1079.         freeDocID(theDocID);
  1080.         return(header);
  1081.       }
  1082.       else{ 
  1083.         waislog(WLOG_HIGH, WLOG_WARNING, 
  1084.             "document <%ld %ld %s> skipped.",
  1085.             doc_entry.start_character,
  1086.             doc_entry.end_character,
  1087.             foo.filename);
  1088.         return(NULL);
  1089.       }
  1090.     }
  1091.       }
  1092.     }
  1093.   }
  1094.   return(NULL);
  1095. }
  1096.  
  1097.  
  1098. /*----------------------------------------------------------------------*/
  1099. /* search for each of the words in a document, up to a limit.
  1100.    this is for relevance feedback. */
  1101.  
  1102. #define MAX_TEXT_SIZE 100000    /* Maximume size of relevant text */
  1103.  
  1104. /* returns true if it added the words, false otherwise (not necessarily 
  1105.    an error) */
  1106. boolean search_for_words_in_document(doc, docid, db, diags, num_diags)
  1107. DocObj* doc;
  1108. long docid;
  1109. database* db;
  1110. diagnosticRecord*** diags;  /* list of diagnostics */
  1111. long *num_diags;
  1112. {
  1113.   char * dbName = db->database_file;
  1114.   long errorCode;
  1115.   WAISDocumentText* doctext;
  1116.  
  1117.   char prefix[MAX_WORD_LENGTH+1];
  1118.   trie *the_dict;
  1119.   trie_allocator* alloc;
  1120.   count_trie_words =0;
  1121.   count_uniq=0;
  1122.     
  1123.   alloc=make_trie_allocator();
  1124.   the_dict = new_trie("",alloc);
  1125.   *prefix = 0;
  1126.  
  1127.   if(doc->Type == NULL ||
  1128.      substrcmp(doc->Type,"TEXT") ||
  1129.      strcmp(doc->Type,"WSRC") == 0 ||
  1130.      strcmp(doc->Type,"WCAT") == 0 ||
  1131.      doc->Type[0] == 0) {
  1132.  
  1133.     doctext = NULL;
  1134.     if (doc->ChunkCode == CT_line)
  1135.       doctext = getDocumentText(doc, &errorCode, NULL);
  1136.     else if ((doc->ChunkCode == CT_byte) ||
  1137.          (doc->ChunkCode == CT_document))
  1138.       doctext = getData(doc, &errorCode, NULL);
  1139.     if (doctext != NULL) {
  1140.  
  1141.       boolean search_result;
  1142.  
  1143.       if(doctext->DocumentText->size > MAX_TEXT_SIZE)
  1144.     doctext->DocumentText->bytes[MAX_TEXT_SIZE] = 0;
  1145.       search_result = prepare_word_list(doctext->DocumentText->bytes,the_dict,alloc);  
  1146. #ifdef FIELDS /* tung, 5/94 */
  1147.       if(db->number_of_fields > 0) {
  1148.     long fcount;
  1149.     for(fcount=0; fcount < db->number_of_fields; fcount++){
  1150.       if(db->fields[fcount].numeric == false)
  1151.         search_result |= search_for_trie_words(the_dict,db,prefix,docid,search_result, db->fields[fcount].field_name);
  1152.     }
  1153.     if(db->dictionary_stream != NULL) {
  1154.       search_result |= search_for_trie_words(the_dict,db,prefix,docid,search_result, FREE_TEXT_FIELD);
  1155.     }
  1156.       } else search_result |= search_for_trie_words(the_dict,db,prefix,docid,search_result, "\0");
  1157. #else
  1158.       search_result |= search_for_trie_words(the_dict,db,prefix,docid,search_result);
  1159. #endif
  1160.       dispose_trie_allocator(alloc);
  1161.  
  1162.       freeWAISDocumentText(doctext);
  1163.       return(search_result);
  1164.     }
  1165.     else { /* bad docid? */
  1166.       DocID* theDocID = NULL;
  1167.       char* local_id = NULL;
  1168.       diagnosticRecord* diag = NULL;
  1169.       char msg[MAX_FILENAME_LEN * 2];
  1170.  
  1171.       theDocID = docIDFromAny(doc->DocumentID);
  1172.       
  1173.       if(theDocID == NULL) {
  1174.     local_id = s_strdup("can't parse docid");
  1175.       }
  1176.       else {
  1177.     local_id = anyToString(GetLocalID(theDocID));
  1178.   
  1179.     freeDocID(theDocID);
  1180.       }
  1181.       waislog(WLOG_HIGH, WLOG_WARNING,
  1182.           "Relevance Feedback with invalid doc-id: '%s'",
  1183.           local_id);
  1184.       strncpy(msg,"Relevant Document not available: ",
  1185.           MAX_FILENAME_LEN);
  1186.       s_strncat(msg,local_id,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1187.       s_free(local_id);
  1188.       (*num_diags)++;
  1189.       diag = makeDiag(true,D_TemporarySystemError,msg);
  1190.       *diags = (diagnosticRecord**)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * *num_diags));
  1191.       (*diags)[(*num_diags)-1] = diag;
  1192.     }
  1193.  
  1194.   }
  1195.   return(false);
  1196. }
  1197.  
  1198.  
  1199. /*----------------------------------------------------------------------*/
  1200. #ifdef FIXDATE
  1201. FixDate(date)
  1202. char *date;
  1203. {
  1204.   long ndate;
  1205.   int year, month, day;
  1206.   ndate = atoi(date);
  1207.   year  = ndate/10000;
  1208.   month = (ndate-10000*year)/100;
  1209.   day   = (ndate-10000*year-100*month);
  1210. #ifdef DEBUG
  1211.   fprintf(stderr, "FixDate: %ld (%d/%d/%d)\n", ndate, year, month, day);
  1212.   fprintf(stderr, "FixDate: %s\n", date);
  1213. #endif
  1214.   if (year>100) strcpy(date,"0");
  1215.   else if (month>12) strcpy(date,"0");
  1216.   else if (day>31)   strcpy(date,"0");
  1217. }
  1218. #endif 
  1219.  
  1220. #ifdef ONELINELENGTH
  1221. FixLength(length)
  1222. int *length;
  1223. {
  1224.   if (*length<MAX_HEADLINE_LEN)
  1225.     *length=0;
  1226. }
  1227. #endif
  1228.  
  1229. WAISDocumentHeader*
  1230. best_hit_to_header(best_hit, maxRawScore, waisProtocolVersion, server, db)
  1231. hit* best_hit;
  1232. long maxRawScore;
  1233. long waisProtocolVersion;
  1234. char *server;
  1235. database* db;
  1236. {
  1237.   long lines,length,count,i;
  1238.   DocID* theDocID = NULL;
  1239.   WAISDocumentHeader* header;
  1240.   char* originName = db->database_file;
  1241.   char local_id[MAX_FILENAME_LEN + 60]; /* filename, start, end */
  1242.   char* tmp_type = NULL;   /* temporary type */
  1243.   char* tmp_type_pointer = NULL;   /* temporary type pointer */
  1244.   local_id[0] = '\0';
  1245.  
  1246.   if (true == calcDocLength(best_hit,&lines,&length))
  1247.     {                /* this document is good, return it */
  1248.       char** type = NULL;
  1249.       long normalScore;
  1250.       if ( _BE_normalized )
  1251.          normalScore = best_hit->weight/MAX_NORMAL_SCORE;
  1252.       else {
  1253.          normalScore = (long)floor(
  1254.                 (((double)best_hit->weight) /
  1255.                  ((double)maxRawScore)) *    
  1256.                 (MAX_NORMAL_SCORE + 1));
  1257.       
  1258.         if (normalScore > MAX_NORMAL_SCORE)
  1259.          normalScore = MAX_NORMAL_SCORE;
  1260.       }
  1261.  
  1262.       sprintf(local_id, "%ld %ld %s", 
  1263.           best_hit->start_character,
  1264.           best_hit->end_character,
  1265.           best_hit->filename);
  1266.          
  1267. /* multitype extensions */
  1268. /* 
  1269.    Need to parse out the document types and add them to the 
  1270.    document type list.
  1271. */
  1272.       if (waisProtocolVersion >= '2') {
  1273.         
  1274.         /* I left this conditional here (it is not really needed, the 
  1275.          * 'else' part could take care of both) on the assumption that
  1276.          * it would be faster for single type documents.
  1277.          */ 
  1278.     if ( strstr(best_hit->type,",") == NULL ) {
  1279.        type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1280.        type[0] = s_strdup(best_hit->type);
  1281.        type[1] = NULL;
  1282.     } else { 
  1283. /* Check for multiple types, including STELAR */
  1284. #ifdef STELAR
  1285.   /*
  1286.    * Use originName to decide whether to look for bitmaps (only database
  1287.    * 'stelar' makes associations between abstracts and bitmaps).
  1288.    */
  1289.       char *dbname;
  1290.       if ((dbname = strrchr(originName, '/')) != NULL)
  1291.         dbname++;
  1292.       else
  1293.         dbname = originName;
  1294.       if ((!strcasecmp(dbname, "STELAR") 
  1295.            || !strcasecmp(dbname, "STELAR-DB")) && BITMAPS) {
  1296.         char *absname, *bitname;
  1297.         char tmpname[80], inrec[80];
  1298.         int found = 0;
  1299.             if ((absname = strrchr(best_hit->filename, '/')) != NULL)
  1300.           strcpy(tmpname, ++absname);
  1301.         else
  1302.           strcpy(tmpname, best_hit->filename);
  1303.         absname = strtok(tmpname, ".");
  1304.         rewind(BITMAPS);
  1305.         do {
  1306.           fgets(inrec, 80, BITMAPS);
  1307.           bitname = strtok(inrec, "\n");
  1308.           if (!strcasecmp(absname, bitname)) {
  1309.         found = 1;
  1310.         break;
  1311.           }
  1312.         } while(!feof(BITMAPS));
  1313.  
  1314.         if (found) {
  1315.           waislog(WLOG_LOW, WLOG_RESULTS,
  1316.               "Bitmap association: %s", absname);
  1317.           type = (char**)s_malloc((size_t)(sizeof(char*) * 4));
  1318.           type[0] = s_strdup(best_hit->type);
  1319.           type[1] = s_strdup("TIFF");
  1320.           type[2] = s_strdup("ARTICLE_TIFF");
  1321.           type[3] = NULL;
  1322.           length = 1024L*1024L;
  1323.         } else {
  1324.           type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1325.           type[0] = s_strdup(best_hit->type);
  1326.           type[1] = NULL;
  1327.         }
  1328.       } else { /* It's not the STELAR database */
  1329. #endif /* STELAR */
  1330.         /* count up the number of document types */
  1331.         count = 1L;
  1332.         for (i = 0L; i < strlen(best_hit->type); i++){
  1333.           if ( best_hit->type[i] == ',' )
  1334.         count++;
  1335.         }
  1336.  
  1337.         /* allocate space for types */
  1338.         type = (char**)s_malloc((size_t)(sizeof(char*) * (count + 1L)));
  1339.    
  1340.         /* duplicate the type and save the pointer */
  1341.         tmp_type = s_strdup(best_hit->type);
  1342.         tmp_type_pointer = tmp_type;
  1343.       
  1344.         /* add types - NULL out the pointer so that strtok can grab the subsequent entries */
  1345.         for (i = 0L; i < count; i++ ) {
  1346.           type[i] = s_strdup(strtok(tmp_type_pointer,","));
  1347.           tmp_type_pointer = NULL;
  1348.         }
  1349.  
  1350.         /* add the terminating null */
  1351.         type[count] = NULL;
  1352.    
  1353.         /* release the tmp_type allocations */
  1354.         s_free(tmp_type);
  1355. #ifdef STELAR
  1356.       }
  1357. #endif /* STELAR */
  1358.     }
  1359.       }
  1360.       else
  1361.     type = NULL;
  1362.       /*
  1363.     printf("header %ld out of %ld\n", *headerNum, 
  1364.     wais_search->MaxDocumentsRetrieved); 
  1365.     */
  1366.       theDocID = makeDocID();
  1367.  
  1368.       theDocID->distributorServer = stringToAny(server);
  1369.       theDocID->originalServer = stringToAny(server);
  1370.           
  1371.       theDocID->distributorDatabase = stringToAny(originName);
  1372.       theDocID->originalDatabase = stringToAny(originName);
  1373.           
  1374.       theDocID->distributorLocalID = stringToAny(local_id);
  1375.       theDocID->originalLocalID = stringToAny(local_id);
  1376. #ifdef FIXDATE
  1377.       FixDate(best_hit->date);
  1378. #endif
  1379. #ifdef ONELINELENGTH
  1380.       FixLength(&length);
  1381. #endif
  1382.       header =
  1383.     makeWAISDocumentHeader(anyFromDocID(theDocID),
  1384.                    UNUSED,
  1385.                    (long)normalScore,
  1386.                    best_hit->best_line,
  1387.                    length,lines,
  1388.                    type,
  1389.                    s_strdup(originName),
  1390.                    s_strdup(best_hit->date),
  1391.                    s_strdup(best_hit->headline),
  1392.                    NULL);
  1393.       freeDocID(theDocID);
  1394.       return(header);
  1395.     }
  1396.   else
  1397.     { 
  1398.       waislog(WLOG_HIGH, WLOG_WARNING, 
  1399.           "document <%ld %ld %s> skipped.",
  1400.           best_hit->start_character,
  1401.           best_hit->end_character,
  1402.           best_hit->filename);
  1403.       return(NULL);
  1404.     }
  1405. }
  1406.  
  1407.  
  1408.  
  1409. /*----------------------------------------------------------------------*/
  1410.  
  1411. boolean run_search(aSearch, headers, diags, index_directory, 
  1412.            seed_words_used, waisProtocolVersion, headerNum)
  1413. SearchAPDU* aSearch;
  1414. WAISDocumentHeader** headers; /* list of results */
  1415. diagnosticRecord*** diags;  /* list of diagnostics */
  1416. char *index_directory;
  1417. char **seed_words_used;  /* called with enough space */
  1418. long waisProtocolVersion;
  1419. long *headerNum;
  1420. /* runs a search on the inverted file index and returns false if it errors 
  1421.    in such a way that it can not even make a diagnostic record 
  1422.    (should not happen).
  1423.    It changes headers with the replies or makes a diagnostic record
  1424.  */
  1425. #ifdef FIELDS /* tung, 1/94 */
  1426.   extern long number_of_operands;
  1427.   extern char* field_name_array[];
  1428.   char field_name[MAX_FILENAME_LEN + 1];
  1429.   char* SeedWords = NULL;
  1430.   long local_number_of_operands = 1;
  1431.   long number_of_fields = 0;
  1432.   int global_dictionary_exists = 0;
  1433. #endif
  1434.   diagnosticRecord* diag = NULL;
  1435.   WAISSearch* wais_search = (WAISSearch*)aSearch->Query; /* for convenience */
  1436.   database* db = NULL;
  1437.   long maxRawScore;
  1438.   long i;
  1439.   query_parameter_type parameters;
  1440.   boolean search_result;
  1441.   char server[255];
  1442.   WAISDocumentHeader* header;
  1443.   long num_diags = 0;
  1444.   char dbName[MAX_FILENAME_LEN * 2];
  1445.  
  1446.   if (aSearch->DatabaseNames == NULL)
  1447.     strcpy(dbName,merge_pathnames(INFO_DATABASE_NAME, index_directory));
  1448.   else
  1449.     strcpy(dbName,merge_pathnames(aSearch->DatabaseNames[0], index_directory));
  1450.  
  1451. #ifdef GET_QUERY_TIMING
  1452.   ftime(&s_time);
  1453. #endif
  1454.  
  1455.   /* strlip .src if it is on the name */
  1456.   if(strlen(dbName) > strlen(".src"))
  1457.     if(0 == strcmp(dbName + strlen(dbName) - strlen(".src"),
  1458.            ".src"))
  1459.       dbName[strlen(dbName) - strlen(".src")] = '\0';
  1460.   
  1461.   if(server_name != NULL)
  1462.     sprintf(server, "%s:%d", server_name, tcp_port);
  1463.   else
  1464.     sprintf(server, "localhost:0");
  1465.  
  1466. #ifdef FIELDS /* tung, 1/94 */
  1467.   /* removed strdup (up) */
  1468.   if((SeedWords = analyse_string(wais_search->SeedWords, 
  1469.                                           &number_of_fields, 
  1470.                                           &global_dictionary_exists)) == NULL) {
  1471.     char msg[MAX_FILENAME_LEN * 2];
  1472.     strncpy(msg,"query syntax error: ", MAX_FILENAME_LEN);
  1473.     s_strncat(msg, wais_search->SeedWords,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1474.     diag = makeDiag(false,D_PermanentSystemError,msg);
  1475.     *diags = (diagnosticRecord **)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * 2));
  1476.     (*diags)[0] = diag;
  1477.     (*diags)[1] = NULL;
  1478.     return(false);
  1479.   }
  1480.   db = openDatabase(dbName, false, true, (number_of_fields > 0)?true:false);
  1481. #else
  1482.   db = openDatabase(dbName, false, true);
  1483. #endif
  1484.   if (db == NULL){
  1485.     char msg[MAX_FILENAME_LEN * 2];
  1486.     strncpy(msg,"The following database is not available: ",
  1487.         MAX_FILENAME_LEN);
  1488.     s_strncat(msg,dbName,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1489.     diag = makeDiag(false,D_PermanentSystemError,msg);
  1490.     *diags = (diagnosticRecord **)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * 2));
  1491.     (*diags)[0] = diag;
  1492.     (*diags)[1] = NULL;
  1493.     return(false);
  1494.   }
  1495.  
  1496. #ifdef FIELDS /* tung, 1/94 */
  1497.   if(number_of_fields > 0) {
  1498.     insert_fields(field_name_array, number_of_fields, db);
  1499.     if(!open_field_streams_for_search(field_name, global_dictionary_exists, db)) {
  1500.       char msg[MAX_FILENAME_LEN * 2];
  1501.       strncpy(msg,"field unexists: ", MAX_FILENAME_LEN);
  1502.       s_strncat(msg, field_name,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1503.       s_strncat(msg, "\n", MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1504.       diag = makeDiag(false,D_PermanentSystemError,msg);
  1505.       *diags = (diagnosticRecord **)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * 2));
  1506.       (*diags)[0] = diag;
  1507.       (*diags)[1] = NULL;
  1508.       disposeDatabase(db);
  1509.       return(false);
  1510.     }
  1511.   }
  1512.   local_number_of_operands = number_of_operands;
  1513.   number_of_operands = 1;
  1514. #endif
  1515.  
  1516. #ifdef BIO        /* dgg */
  1517.   {
  1518.     char *cp= read_delimiters( db);  /* use data-specific delimiters, if available */
  1519.     if (cp != NULL) { 
  1520.       strcpy( gDelimiters, cp); 
  1521.       wordDelimiter= wordbreak_user; 
  1522.       }
  1523.     else 
  1524.       wordDelimiter= wordbreak_notalnum;
  1525.   }
  1526. #else
  1527.   wordDelimiter= wordbreak_notalnum;  /* actually, wordDelimeter is used only ifdef BIO ? */
  1528. #endif
  1529.  
  1530.   /* figure out if it is a NEXT or PREVIOUS, if so, return it. */
  1531.   header = handle_next_and_previous(wais_search->Docs, db, 
  1532.                     waisProtocolVersion, server);
  1533.   if(header != NULL){
  1534.     headers[(*headerNum)++] = header;
  1535.     headers[*headerNum] = NULL;
  1536.     return(true);
  1537.   }
  1538.   
  1539.   /* until seed_words_used is supported */
  1540.   strcpy(*seed_words_used, wais_search->SeedWords);
  1541.  
  1542.   parameters.max_hit_retrieved = wais_search->MaxDocumentsRetrieved;
  1543.   set_query_parameter(SET_MAX_RETRIEVED_MASK, ¶meters);
  1544.  
  1545.   search_result = false;
  1546.   init_search_word(db);
  1547.  
  1548. #ifdef RELEVANCE_FEEDBACK
  1549.   if(wais_search->Docs != NULL) {
  1550.     DocObj* doc = NULL;
  1551.     boolean res;
  1552.     /* assemble the elements and construct a response */
  1553.     for (i = 0, doc = wais_search->Docs[i]; 
  1554.      doc != NULL; 
  1555.      doc = wais_search->Docs[++i]){ 
  1556.       search_result |= 
  1557.     search_for_words_in_document(doc,i+1,db,diags,&num_diags);
  1558.     }
  1559.     if (*diags != NULL) {
  1560.     num_diags++;
  1561.     *diags = (diagnosticRecord**)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * num_diags));
  1562.     (*diags)[num_diags-1] = NULL;
  1563.       }
  1564.   }
  1565. #endif                /* RELEVANT_FEEDBACK */
  1566.  
  1567.  
  1568. #ifdef FIELDS /* tung, 1/94 */
  1569.   number_of_operands = local_number_of_operands;
  1570.   search_result |= search_for_words(SeedWords, db, 0);
  1571.   if(SeedWords != NULL) s_free(SeedWords);
  1572. #else
  1573.   search_result |= search_for_words(wais_search->SeedWords, db, 0);
  1574. #endif
  1575.  
  1576.   if (search_result == true){ /* the search went ok */
  1577.       hit best_hit;
  1578.       finished_search_word(db);
  1579.       init_best_hit(db);
  1580.       for (i = 0; i < wais_search->MaxDocumentsRetrieved; i++){ 
  1581.     if(0 != next_best_hit(&best_hit, db))
  1582.       break;        /* out of hits */
  1583.     if(i == 0)
  1584.       maxRawScore = best_hit.weight;
  1585.     if (best_hit.weight > 0){
  1586.       WAISDocumentHeader* header = 
  1587.         best_hit_to_header(&best_hit, maxRawScore,
  1588.                    waisProtocolVersion,server,db);
  1589.       if(NULL != header){
  1590.         headers[(*headerNum)++] = header;
  1591.         headers[*headerNum] = NULL;
  1592.       }
  1593.     }
  1594.       }
  1595.     }
  1596.   else
  1597.     {                /* something went awry in the search */
  1598.       num_diags++;
  1599.       diag = makeDiag(true,D_PermanentSystemError,
  1600.                       "Serious error in server");
  1601.       *diags = (diagnosticRecord**)
  1602.     s_realloc(*diags, (size_t)(sizeof(diagnosticRecord*) * num_diags));
  1603.       (*diags)[num_diags-2] = diag;
  1604.       (*diags)[num_diags-1] = NULL;
  1605.     }
  1606.   finished_best_hit(db);
  1607.   /* free everything */
  1608.   closeDatabase(db);
  1609.   disposeDatabase(db);           /* (up) */
  1610. #ifdef GET_QUERY_TIMING
  1611.   ftime(&e_time);
  1612.   t_time += (e_time.time + e_time.millitm/1000.0) - 
  1613.             (s_time.time + s_time.millitm/1000.0);
  1614.   n_query++;
  1615.   if ( n_query == 200 ) {
  1616.    waislog(WLOG_LOW, WLOG_INFO, "searching 200 queries takes %f seconds.",
  1617.            t_time);
  1618.    waislog(WLOG_LOW, WLOG_INFO, "average %f/query.", t_time/200.0);
  1619.    n_query = 0;
  1620.    t_time = 0;
  1621.    }
  1622. #endif
  1623.  
  1624.   return(true);
  1625. }
  1626.